package tech

import (
	"math/rand"
	"time"
)

func (s *Solve) removeCells(g *Grid) {
	cellxys := make([][2]int, 0, 81)
	for i := 0; i < 9; i++ {
		for j := 0; j < 9; j++ {
			cellxys = append(cellxys, [2]int{i, j})
		}
	}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	r.Shuffle(len(cellxys), func(i, j int) {
		cellxys[i], cellxys[j] = cellxys[j], cellxys[i]
	})
	for _, xy := range cellxys {
		before := Grid(*g)
		g.cells[xy[0]][xy[1]].setValue(0)
		g.solvedCount--

		after := Grid(*g)
		//trial.Init()
		//trial.Clearup()
		//trial.prepareStrategies()
		//fmt.Println(len(trial.strategies), trial.strategyNames)
		g.Init()
		g.Clearup()
		//ok := s.mBacktrack(g)
		ok, _ := s.Make(g)
		if !ok || !g.Validate() {
			*g = before
		} else {
			*g = after
		}

	}
}

func GenerateGrid(in string) *Grid {
	g := &Grid{}
	g.Decode(in)
	g.Init()
	g.Clearup()

	s := NewSolve()

	s.removeCells(g)
	//g.printSimply()
	//fmt.Printf("The cues=%v\n", g.solvedCount)
	return g
}

func (s *Solve) removeNormalCell(g *Grid, xy [2]int) (ok bool, cg *Grid) {
	before := Grid(*g)
	g.cells[xy[0]][xy[1]].setValue(0)
	g.solvedCount--

	after := Grid(*g)
	g.Init()
	g.Clearup()
	//ok := s.mBacktrack(g)
	ok, _ = s.Make(g)
	if !ok || !g.Validate() {
		cg = &before
	} else {
		cg = &after
	}
	return
}

func (s *Solve) removeAdvancedCell(g *Grid, xy [2]int, advanced int) (ok bool, cg *Grid) {
	before := Grid(*g)
	g.cells[xy[0]][xy[1]].setValue(0)
	g.solvedCount--

	after := Grid(*g)
	g.Init()
	g.Clearup()
	//ok := s.mBacktrack(g)
	ok, _ = s.AMake(g, advanced)
	if !ok || !g.Validate() {
		cg = &before
	} else {
		cg = &after
	}
	return
}
func (s *Solve) aremoveCells(g *Grid, advenced int) {
	cellxys := make([][2]int, 0, 81)
	for i := 0; i < 9; i++ {
		for j := 0; j < 9; j++ {
			cellxys = append(cellxys, [2]int{i, j})
		}
	}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	r.Shuffle(len(cellxys), func(i, j int) {
		cellxys[i], cellxys[j] = cellxys[j], cellxys[i]
	})
	for _, xy := range cellxys {
		before := Grid(*g)
		g.cells[xy[0]][xy[1]].setValue(0)
		g.solvedCount--

		after := Grid(*g)
		//trial.Init()
		//trial.Clearup()
		//trial.prepareStrategies()
		//fmt.Println(len(trial.strategies), trial.strategyNames)
		g.Init()
		g.Clearup()
		//ok := s.mBacktrack(g)
		ok, _ := s.AMake(g, advenced)
		if !ok || !g.Validate() {
			*g = before
		} else {
			*g = after
		}

	}
}

func (s *Solve) AMake(g *Grid, advanced int) (finished bool, results []*Result) {
	OUTPUTRESULT = true
	EXHAUSTIVE = false
	CHANGE = true

	used := false
	n := 0
	for !g.Solved() {
		for i, t := range s.techiques {
			ok, rs := t(g)
			if ok && len(rs) > 0 && i >= advanced {
				used = true
				results = append(results, rs[0])
				n++
				break
			}
		}

		if !used {
			//fmt.Println("No technique can be used.")
			break
		}
		used = false
		if n > 300 {
			break
		}
	}
	finished = g.Solved()
	//if finished {
	//	PrintResults(results)
	//}
	return
}

func AGenerateGrid(in string) *Grid {
	g := &Grid{}
	g.Decode(in)
	g.Init()
	g.Clearup()

	s := &Solve{}
	s.techiques = []Techique{
		//s.LastOne,
		s.HiddenSingleBox,
		s.HiddenSingleRow,
		s.HiddenSingleCol,
		s.NakedSingle,
		//s.HiddenPair,
		//s.NakedPair,
		//s.HiddenTriple,
		//s.NakedTriple,
		//s.HiddenQuandruple,
		//s.NakedQuadruple,
		//g.HiddenSingleCol,
		//g.NakedSingle,
	}
	advanced := 4
	s.aremoveCells(g, advanced)
	//g.printSimply()
	//fmt.Printf("The cues=%v\n", g.solvedCount)
	return g
}

func (s *Solve) mBacktrack(g *Grid) (used bool) {
	results := []Grid{}
	results = g.backtrack(results)
	n := len(results)
	switch {
	case n == 0:
		return false
	case n == 1:
		*g = results[0]
		return true
	default:
		for _, r := range results {
			r.PrintGrid()
		}

		*g = results[0]
		return false
	}
}

func (g *Grid) backtrack(results []Grid) []Grid {
	g.Init()
	g.Clearup()
	if !g.filledCorrenctly() {
		return []Grid{}
	}

	g.basicTech()
	//fmt.Println("#1 solvedCount", g.solvedCount)
	if g.solvedCount == 81 {
		results = append(results, *g)
		return results
	}
	//fmt.Println("#2 solvedCount", g.solvedCount)
	cell := g.guessCell()
	//fmt.Println("guess", cell)
	//fmt.Println("0-results=", len(results))
	for _, cand := range cell.cands.GetList() {
		//fmt.Println(cell, cand, len(results))
		x := g
		x.Init()
		x.Clearup()
		x.solveOneCell(cell.rowNum, cell.colNum, toValue(cand))
		r := x.backtrack(results)
		if len(r) > 0 {
			results = r
		}
	}
	//fmt.Println("1-results=", len(results))
	return results
}
